home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / lib / tk2.3 / dist / tkEntry.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-08-21  |  53.9 KB  |  1,780 lines

  1. /* 
  2.  * tkEntry.c --
  3.  *
  4.  *    This module implements entry widgets for the Tk
  5.  *    toolkit.  An entry displays a string and allows
  6.  *    the string to be edited.
  7.  *
  8.  * Copyright 1990 Regents of the University of California.
  9.  * Permission to use, copy, modify, and distribute this
  10.  * software and its documentation for any purpose and without
  11.  * fee is hereby granted, provided that the above copyright
  12.  * notice appear in all copies.  The University of California
  13.  * makes no representations about the suitability of this
  14.  * software for any purpose.  It is provided "as is" without
  15.  * express or implied warranty.
  16.  */
  17.  
  18. #ifndef lint
  19. static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkEntry.c,v 1.37 92/08/21 16:09:15 ouster Exp $ SPRITE (Berkeley)";
  20. #endif
  21.  
  22. #include "default.h"
  23. #include "tkConfig.h"
  24. #include "tkInt.h"
  25.  
  26. /*
  27.  * A data structure of the following type is kept for each entry
  28.  * widget managed by this file:
  29.  */
  30.  
  31. typedef struct {
  32.     Tk_Window tkwin;        /* Window that embodies the entry. NULL
  33.                  * means that the window has been destroyed
  34.                  * but the data structures haven't yet been
  35.                  * cleaned up.*/
  36.     Tcl_Interp *interp;        /* Interpreter associated with entry. */
  37.     int numChars;        /* Number of non-NULL characters in
  38.                  * string (may be 0). */
  39.     char *string;        /* Pointer to storage for string;
  40.                  * NULL-terminated;  malloc-ed. */
  41.     char *textVarName;        /* Name of variable (malloc'ed) or NULL.
  42.                  * If non-NULL, entry's string tracks the
  43.                  * contents of this variable and vice versa. */
  44.     Tk_Uid state;        /* Normal or disabled.  Entry is read-only
  45.                  * when disabled. */
  46.  
  47.     /*
  48.      * Information used when displaying widget:
  49.      */
  50.  
  51.     Tk_3DBorder normalBorder;    /* Used for drawing border around whole
  52.                  * window, plus used for background. */
  53.     int borderWidth;        /* Width of 3-D border around window. */
  54.     int relief;            /* 3-D effect: TK_RELIEF_RAISED, etc. */
  55.     XFontStruct *fontPtr;    /* Information about text font, or NULL. */
  56.     XColor *fgColorPtr;        /* Text color in normal mode. */
  57.     GC textGC;            /* For drawing normal text. */
  58.     Tk_3DBorder selBorder;    /* Border and background for selected
  59.                  * characters. */
  60.     int selBorderWidth;        /* Width of border around selection. */
  61.     XColor *selFgColorPtr;    /* Foreground color for selected text. */
  62.     GC selTextGC;        /* For drawing selected text. */
  63.     Tk_3DBorder cursorBorder;    /* Used to draw vertical bar for insertion
  64.                  * cursor. */
  65.     int cursorWidth;        /* Total width of insert cursor. */
  66.     int cursorBorderWidth;    /* Width of 3-D border around insert cursor. */
  67.     int cursorOnTime;        /* Number of milliseconds cursor should spend
  68.                  * in "on" state for each blink. */
  69.     int cursorOffTime;        /* Number of milliseconds cursor should spend
  70.                  * in "off" state for each blink. */
  71.     Tk_TimerToken cursorBlinkHandler;
  72.                 /* Timer handler used to blink cursor on and
  73.                  * off. */
  74.     int avgWidth;        /* Width of average character. */
  75.     int prefWidth;        /* Desired width of window, measured in
  76.                  * average characters. */
  77.     int offset;            /* 0 if window is flat, or borderWidth if
  78.                  * raised or sunken. */
  79.     int leftIndex;        /* Index of left-most character visible in
  80.                  * window. */
  81.     int cursorPos;        /* Index of character before which next
  82.                  * typed character will be inserted. */
  83.  
  84.     /*
  85.      * Information about what's selected, if any.
  86.      */
  87.  
  88.     int selectFirst;        /* Index of first selected character (-1 means
  89.                  * nothing selected. */
  90.     int selectLast;        /* Index of last selected character (-1 means
  91.                  * nothing selected. */
  92.     int selectAnchor;        /* Fixed end of selection (i.e. "select to"
  93.                  * operation will use this as one end of the
  94.                  * selection). */
  95.     int exportSelection;    /* Non-zero means tie internal entry selection
  96.                  * to X selection. */
  97.  
  98.     /*
  99.      * Information for scanning:
  100.      */
  101.  
  102.     int scanMarkX;        /* X-position at which scan started (e.g.
  103.                  * button was pressed here). */
  104.     int scanMarkIndex;        /* Index of character that was at left of
  105.                  * window when scan started. */
  106.  
  107.     /*
  108.      * Miscellaneous information:
  109.      */
  110.  
  111.     Cursor cursor;        /* Current cursor for window, or None. */
  112.     char *scrollCmd;        /* Command prefix for communicating with
  113.                  * scrollbar(s).  Malloc'ed.  NULL means
  114.                  * no command to issue. */
  115.     int flags;            /* Miscellaneous flags;  see below for
  116.                  * definitions. */
  117. } Entry;
  118.  
  119. /*
  120.  * Assigned bits of "flags" fields of Entry structures, and what those
  121.  * bits mean:
  122.  *
  123.  * REDRAW_PENDING:        Non-zero means a DoWhenIdle handler has
  124.  *                already been queued to redisplay the entry.
  125.  * BORDER_NEEDED:        Non-zero means 3-D border must be redrawn
  126.  *                around window during redisplay.  Normally
  127.  *                only text portion needs to be redrawn.
  128.  * CURSOR_ON:            Non-zero means cursor is displayed at
  129.  *                present.  0 means it isn't displayed.
  130.  * GOT_FOCUS:            Non-zero means this window has the input
  131.  *                focus.
  132.  */
  133.  
  134. #define REDRAW_PENDING        1
  135. #define BORDER_NEEDED        2
  136. #define CURSOR_ON        4
  137. #define GOT_FOCUS        8
  138.  
  139. /*
  140.  * Information used for argv parsing.
  141.  */
  142.  
  143. static Tk_ConfigSpec configSpecs[] = {
  144.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  145.     DEF_ENTRY_BG_COLOR, Tk_Offset(Entry, normalBorder),
  146.     TK_CONFIG_COLOR_ONLY},
  147.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  148.     DEF_ENTRY_BG_MONO, Tk_Offset(Entry, normalBorder),
  149.     TK_CONFIG_MONO_ONLY},
  150.     {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
  151.     (char *) NULL, 0, 0},
  152.     {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
  153.     (char *) NULL, 0, 0},
  154.     {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
  155.     DEF_ENTRY_BORDER_WIDTH, Tk_Offset(Entry, borderWidth), 0},
  156.     {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
  157.     DEF_ENTRY_CURSOR, Tk_Offset(Entry, cursor), TK_CONFIG_NULL_OK},
  158.     {TK_CONFIG_BORDER, "-cursorbackground", "cursorBackground", "Foreground",
  159.     DEF_ENTRY_CURSOR_BG, Tk_Offset(Entry, cursorBorder), 0},
  160.     {TK_CONFIG_PIXELS, "-cursorborderwidth", "cursorBorderWidth", "BorderWidth",
  161.     DEF_ENTRY_CURSOR_BD_COLOR, Tk_Offset(Entry, cursorBorderWidth),
  162.     TK_CONFIG_COLOR_ONLY},
  163.     {TK_CONFIG_PIXELS, "-cursorborderwidth", "cursorBorderWidth", "BorderWidth",
  164.     DEF_ENTRY_CURSOR_BD_MONO, Tk_Offset(Entry, cursorBorderWidth),
  165.     TK_CONFIG_MONO_ONLY},
  166.     {TK_CONFIG_INT, "-cursorofftime", "cursorOffTime", "OffTime",
  167.     DEF_ENTRY_CURSOR_OFF_TIME, Tk_Offset(Entry, cursorOffTime), 0},
  168.     {TK_CONFIG_INT, "-cursorontime", "cursorOnTime", "OnTime",
  169.     DEF_ENTRY_CURSOR_ON_TIME, Tk_Offset(Entry, cursorOnTime), 0},
  170.     {TK_CONFIG_PIXELS, "-cursorwidth", "cursorWidth", "CursorWidth",
  171.     DEF_ENTRY_CURSOR_WIDTH, Tk_Offset(Entry, cursorWidth), 0},
  172.     {TK_CONFIG_BOOLEAN, "-exportselection", "exportSelection",
  173.     "ExportSelection", DEF_ENTRY_EXPORT_SELECTION,
  174.     Tk_Offset(Entry, exportSelection), 0},
  175.     {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
  176.     (char *) NULL, 0, 0},
  177.     {TK_CONFIG_FONT, "-font", "font", "Font",
  178.     DEF_ENTRY_FONT, Tk_Offset(Entry, fontPtr), 0},
  179.     {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
  180.     DEF_ENTRY_FG, Tk_Offset(Entry, fgColorPtr), 0},
  181.     {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
  182.     DEF_ENTRY_RELIEF, Tk_Offset(Entry, relief), 0},
  183.     {TK_CONFIG_STRING, "-scrollcommand", "scrollCommand", "ScrollCommand",
  184.     DEF_ENTRY_SCROLL_COMMAND, Tk_Offset(Entry, scrollCmd), 0},
  185.     {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
  186.     DEF_ENTRY_SELECT_COLOR, Tk_Offset(Entry, selBorder),
  187.     TK_CONFIG_COLOR_ONLY},
  188.     {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
  189.     DEF_ENTRY_SELECT_MONO, Tk_Offset(Entry, selBorder),
  190.     TK_CONFIG_MONO_ONLY},
  191.     {TK_CONFIG_PIXELS, "-selectborderwidth", "selectBorderWidth", "BorderWidth",
  192.     DEF_ENTRY_SELECT_BD_COLOR, Tk_Offset(Entry, selBorderWidth),
  193.     TK_CONFIG_COLOR_ONLY},
  194.     {TK_CONFIG_PIXELS, "-selectborderwidth", "selectBorderWidth", "BorderWidth",
  195.     DEF_ENTRY_SELECT_BD_MONO, Tk_Offset(Entry, selBorderWidth),
  196.     TK_CONFIG_MONO_ONLY},
  197.     {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
  198.     DEF_ENTRY_SELECT_FG_COLOR, Tk_Offset(Entry, selFgColorPtr),
  199.     TK_CONFIG_COLOR_ONLY},
  200.     {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
  201.     DEF_ENTRY_SELECT_FG_MONO, Tk_Offset(Entry, selFgColorPtr),
  202.     TK_CONFIG_MONO_ONLY},
  203.     {TK_CONFIG_UID, "-state", "state", "State",
  204.     DEF_ENTRY_STATE, Tk_Offset(Entry, state), 0},
  205.     {TK_CONFIG_STRING, "-textvariable", "textVariable", "Variable",
  206.     DEF_ENTRY_TEXT_VARIABLE, Tk_Offset(Entry, textVarName),
  207.     TK_CONFIG_NULL_OK},
  208.     {TK_CONFIG_INT, "-width", "width", "Width",
  209.     DEF_ENTRY_WIDTH, Tk_Offset(Entry, prefWidth), 0},
  210.     {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
  211.     (char *) NULL, 0, 0}
  212. };
  213.  
  214. /*
  215.  * Flags for GetEntryIndex procedure:
  216.  */
  217.  
  218. #define ZERO_OK            1
  219. #define LAST_PLUS_ONE_OK    2
  220.  
  221. /*
  222.  * Forward declarations for procedures defined later in this file:
  223.  */
  224.  
  225. static int        ConfigureEntry _ANSI_ARGS_((Tcl_Interp *interp,
  226.                 Entry *entryPtr, int argc, char **argv,
  227.                 int flags));
  228. static void        DeleteChars _ANSI_ARGS_((Entry *entryPtr, int index,
  229.                 int count));
  230. static void        DestroyEntry _ANSI_ARGS_((ClientData clientData));
  231. static void        DisplayEntry _ANSI_ARGS_((ClientData clientData));
  232. static int        GetEntryIndex _ANSI_ARGS_((Tcl_Interp *interp,
  233.                 Entry *entryPtr, char *string, int *indexPtr));
  234. static void        InsertChars _ANSI_ARGS_((Entry *entryPtr, int index,
  235.                 char *string));
  236. static void        EntryBlinkProc _ANSI_ARGS_((ClientData clientData));
  237. static void        EntryEventProc _ANSI_ARGS_((ClientData clientData,
  238.                 XEvent *eventPtr));
  239. static void        EntryFocusProc _ANSI_ARGS_ ((ClientData clientData,
  240.                 int gotFocus));
  241. static int        EntryFetchSelection _ANSI_ARGS_((ClientData clientData,
  242.                 int offset, char *buffer, int maxBytes));
  243. static void        EntryLostSelection _ANSI_ARGS_((
  244.                 ClientData clientData));
  245. static void        EventuallyRedraw _ANSI_ARGS_((Entry *entryPtr));
  246. static void        EntryScanTo _ANSI_ARGS_((Entry *entryPtr, int y));
  247. static void        EntrySetValue _ANSI_ARGS_((Entry *entryPtr,
  248.                 char *value));
  249. static void        EntrySelectTo _ANSI_ARGS_((
  250.                 Entry *entryPtr, int index));
  251. static char *        EntryTextVarProc _ANSI_ARGS_((ClientData clientData,
  252.                 Tcl_Interp *interp, char *name1, char *name2,
  253.                 int flags));
  254. static void        EntryUpdateScrollbar _ANSI_ARGS_((Entry *entryPtr));
  255. static int        EntryWidgetCmd _ANSI_ARGS_((ClientData clientData,
  256.                 Tcl_Interp *interp, int argc, char **argv));
  257.  
  258. /*
  259.  *--------------------------------------------------------------
  260.  *
  261.  * Tk_EntryCmd --
  262.  *
  263.  *    This procedure is invoked to process the "entry" Tcl
  264.  *    command.  See the user documentation for details on what
  265.  *    it does.
  266.  *
  267.  * Results:
  268.  *    A standard Tcl result.
  269.  *
  270.  * Side effects:
  271.  *    See the user documentation.
  272.  *
  273.  *--------------------------------------------------------------
  274.  */
  275.  
  276. int
  277. Tk_EntryCmd(clientData, interp, argc, argv)
  278.     ClientData clientData;    /* Main window associated with
  279.                  * interpreter. */
  280.     Tcl_Interp *interp;        /* Current interpreter. */
  281.     int argc;            /* Number of arguments. */
  282.     char **argv;        /* Argument strings. */
  283. {
  284.     Tk_Window tkwin = (Tk_Window) clientData;
  285.     register Entry *entryPtr;
  286.     Tk_Window new;
  287.  
  288.     if (argc < 2) {
  289.     Tcl_AppendResult(interp, "wrong # args:  should be \"",
  290.         argv[0], " pathName ?options?\"", (char *) NULL);
  291.     return TCL_ERROR;
  292.     }
  293.  
  294.     new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
  295.     if (new == NULL) {
  296.     return TCL_ERROR;
  297.     }
  298.  
  299.     /*
  300.      * Initialize the fields of the structure that won't be initialized
  301.      * by ConfigureEntry, or that ConfigureEntry requires to be
  302.      * initialized already (e.g. resource pointers).
  303.      */
  304.  
  305.     entryPtr = (Entry *) ckalloc(sizeof(Entry));
  306.     entryPtr->tkwin = new;
  307.     entryPtr->interp = interp;
  308.     entryPtr->numChars = 0;
  309.     entryPtr->string = (char *) ckalloc(1);
  310.     entryPtr->string[0] = '\0';
  311.     entryPtr->textVarName = NULL;
  312.     entryPtr->state = tkNormalUid;
  313.     entryPtr->normalBorder = NULL;
  314.     entryPtr->fontPtr = NULL;
  315.     entryPtr->fgColorPtr = NULL;
  316.     entryPtr->textGC = None;
  317.     entryPtr->selBorder = NULL;
  318.     entryPtr->selFgColorPtr = NULL;
  319.     entryPtr->selTextGC = NULL;
  320.     entryPtr->cursorBorder = NULL;
  321.     entryPtr->cursorBlinkHandler = (Tk_TimerToken) NULL;
  322.     entryPtr->leftIndex = 0;
  323.     entryPtr->cursorPos = 0;
  324.     entryPtr->selectFirst = -1;
  325.     entryPtr->selectLast = -1;
  326.     entryPtr->selectAnchor = 0;
  327.     entryPtr->exportSelection = 1;
  328.     entryPtr->scanMarkX = 0;
  329.     entryPtr->cursor = None;
  330.     entryPtr->scrollCmd = NULL;
  331.     entryPtr->flags = 0;
  332.  
  333.     Tk_SetClass(entryPtr->tkwin, "Entry");
  334.     Tk_CreateEventHandler(entryPtr->tkwin, ExposureMask|StructureNotifyMask,
  335.         EntryEventProc, (ClientData) entryPtr);
  336.     Tk_CreateSelHandler(entryPtr->tkwin, XA_STRING, EntryFetchSelection,
  337.         (ClientData) entryPtr, XA_STRING);
  338.     Tcl_CreateCommand(interp, Tk_PathName(entryPtr->tkwin), EntryWidgetCmd,
  339.         (ClientData) entryPtr, (void (*)()) NULL);
  340.     if (ConfigureEntry(interp, entryPtr, argc-2, argv+2, 0) != TCL_OK) {
  341.     goto error;
  342.     }
  343.     Tk_CreateFocusHandler(entryPtr->tkwin, EntryFocusProc,
  344.         (ClientData) entryPtr);
  345.  
  346.     interp->result = Tk_PathName(entryPtr->tkwin);
  347.     return TCL_OK;
  348.  
  349.     error:
  350.     Tk_DestroyWindow(entryPtr->tkwin);
  351.     return TCL_ERROR;
  352. }
  353.  
  354. /*
  355.  *--------------------------------------------------------------
  356.  *
  357.  * EntryWidgetCmd --
  358.  *
  359.  *    This procedure is invoked to process the Tcl command
  360.  *    that corresponds to a widget managed by this module.
  361.  *    See the user documentation for details on what it does.
  362.  *
  363.  * Results:
  364.  *    A standard Tcl result.
  365.  *
  366.  * Side effects:
  367.  *    See the user documentation.
  368.  *
  369.  *--------------------------------------------------------------
  370.  */
  371.  
  372. static int
  373. EntryWidgetCmd(clientData, interp, argc, argv)
  374.     ClientData clientData;        /* Information about entry widget. */
  375.     Tcl_Interp *interp;            /* Current interpreter. */
  376.     int argc;                /* Number of arguments. */
  377.     char **argv;            /* Argument strings. */
  378. {
  379.     register Entry *entryPtr = (Entry *) clientData;
  380.     int result = TCL_OK;
  381.     int length;
  382.     char c;
  383.  
  384.     if (argc < 2) {
  385.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  386.         argv[0], " option ?arg arg ...?\"", (char *) NULL);
  387.     return TCL_ERROR;
  388.     }
  389.     Tk_Preserve((ClientData) entryPtr);
  390.     c = argv[1][0];
  391.     length = strlen(argv[1]);
  392.     if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
  393.         && (length >= 2)) {
  394.     if (argc == 2) {
  395.         result = Tk_ConfigureInfo(interp, entryPtr->tkwin, configSpecs,
  396.             (char *) entryPtr, (char *) NULL, 0);
  397.     } else if (argc == 3) {
  398.         result = Tk_ConfigureInfo(interp, entryPtr->tkwin, configSpecs,
  399.             (char *) entryPtr, argv[2], 0);
  400.     } else {
  401.         result = ConfigureEntry(interp, entryPtr, argc-2, argv+2,
  402.             TK_CONFIG_ARGV_ONLY);
  403.     }
  404.     } else if ((c == 'c') && (strncmp(argv[1], "cursor", length) == 0)
  405.         && (length >= 2)) {
  406.     if (argc != 3) {
  407.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  408.             argv[0], " cursor pos\"",
  409.             (char *) NULL);
  410.         goto error;
  411.     }
  412.     if (GetEntryIndex(interp, entryPtr, argv[2], &entryPtr->cursorPos)
  413.         != TCL_OK) {
  414.         goto error;
  415.     }
  416.     EventuallyRedraw(entryPtr);
  417.     } else if ((c == 'd') && (strncmp(argv[1], "delete", length) == 0)) {
  418.     int first, last;
  419.  
  420.     if ((argc < 3) || (argc > 4)) {
  421.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  422.             argv[0], " delete firstIndex ?lastIndex?\"",
  423.             (char *) NULL);
  424.         goto error;
  425.     }
  426.     if (GetEntryIndex(interp, entryPtr, argv[2], &first) != TCL_OK) {
  427.         goto error;
  428.     }
  429.     if (argc == 3) {
  430.         last = first;
  431.     } else {
  432.         if (GetEntryIndex(interp, entryPtr, argv[3], &last) != TCL_OK) {
  433.         goto error;
  434.         }
  435.     }
  436.     if ((last >= first) && (entryPtr->state == tkNormalUid)) {
  437.         DeleteChars(entryPtr, first, last+1-first);
  438.     }
  439.     } else if ((c == 'g') && (strncmp(argv[1], "get", length) == 0)) {
  440.     if (argc != 2) {
  441.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  442.             argv[0], " get\"", (char *) NULL);
  443.         goto error;
  444.     }
  445.     interp->result = entryPtr->string;
  446.     } else if ((c == 'i') && (strncmp(argv[1], "index", length) == 0)
  447.         && (length >= 2)) {
  448.     int index;
  449.  
  450.     if (argc != 3) {
  451.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  452.             argv[0], " index string\"", (char *) NULL);
  453.         goto error;
  454.     }
  455.     if (GetEntryIndex(interp, entryPtr, argv[2], &index) != TCL_OK) {
  456.         goto error;
  457.     }
  458.     sprintf(interp->result, "%d", index);
  459.     } else if ((c == 'i') && (strncmp(argv[1], "insert", length) == 0)
  460.         && (length >= 2)) {
  461.     int index;
  462.  
  463.     if (argc != 4) {
  464.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  465.             argv[0], " insert index text\"",
  466.             (char *) NULL);
  467.         goto error;
  468.     }
  469.     if (GetEntryIndex(interp, entryPtr, argv[2], &index) != TCL_OK) {
  470.         goto error;
  471.     }
  472.     if (entryPtr->state == tkNormalUid) {
  473.         InsertChars(entryPtr, index, argv[3]);
  474.     }
  475.     } else if ((c == 's') && (length >= 2)
  476.         && (strncmp(argv[1], "scan", length) == 0)) {
  477.     int x;
  478.  
  479.     if (argc != 4) {
  480.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  481.             argv[0], " scan mark|dragto x\"", (char *) NULL);
  482.         goto error;
  483.     }
  484.     if (Tcl_GetInt(interp, argv[3], &x) != TCL_OK) {
  485.         goto error;
  486.     }
  487.     if ((argv[2][0] == 'm')
  488.         && (strncmp(argv[2], "mark", strlen(argv[2])) == 0)) {
  489.         entryPtr->scanMarkX = x;
  490.         entryPtr->scanMarkIndex = entryPtr->leftIndex;
  491.     } else if ((argv[2][0] == 'd')
  492.         && (strncmp(argv[2], "dragto", strlen(argv[2])) == 0)) {
  493.         EntryScanTo(entryPtr, x);
  494.     } else {
  495.         Tcl_AppendResult(interp, "bad scan option \"", argv[2],
  496.             "\":  must be mark or dragto", (char *) NULL);
  497.         goto error;
  498.     }
  499.     } else if ((c == 's') && (length >= 2)
  500.         && (strncmp(argv[1], "select", length) == 0)) {
  501.     int index;
  502.  
  503.     if (argc < 3) {
  504.         Tcl_AppendResult(interp, "too few args: should be \"",
  505.             argv[0], " select option ?index?\"", (char *) NULL);
  506.         goto error;
  507.     }
  508.     length = strlen(argv[2]);
  509.     c = argv[2][0];
  510.     if ((c == 'c') && (argv[2] != NULL)
  511.         && (strncmp(argv[2], "clear", length) == 0)) {
  512.         if (argc != 3) {
  513.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  514.             argv[0], " select clear\"", (char *) NULL);
  515.         goto error;
  516.         }
  517.         if (entryPtr->selectFirst != -1) {
  518.         entryPtr->selectFirst = entryPtr->selectLast = -1;
  519.         EventuallyRedraw(entryPtr);
  520.         }
  521.         goto done;
  522.     }
  523.     if (argc >= 4) {
  524.         if (GetEntryIndex(interp, entryPtr, argv[3], &index) != TCL_OK) {
  525.         goto error;
  526.         }
  527.     }
  528.     if ((c == 'a') && (strncmp(argv[2], "adjust", length) == 0)) {
  529.         if (argc != 4) {
  530.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  531.             argv[0], " select adjust index\"",
  532.             (char *) NULL);
  533.         goto error;
  534.         }
  535.         if (entryPtr->selectFirst >= 0) {
  536.         if (index < (entryPtr->selectFirst + entryPtr->selectLast)/2) {
  537.             entryPtr->selectAnchor = entryPtr->selectLast + 1;
  538.         } else {
  539.             entryPtr->selectAnchor = entryPtr->selectFirst;
  540.         }
  541.         }
  542.         EntrySelectTo(entryPtr, index);
  543.     } else if ((c == 'f') && (strncmp(argv[2], "from", length) == 0)) {
  544.         if (argc != 4) {
  545.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  546.             argv[0], " select from index\"",
  547.             (char *) NULL);
  548.         goto error;
  549.         }
  550.         entryPtr->selectAnchor = index;
  551.     } else if ((c == 't') && (strncmp(argv[2], "to", length) == 0)) {
  552.         if (argc != 4) {
  553.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  554.             argv[0], " select to index\"",
  555.             (char *) NULL);
  556.         goto error;
  557.         }
  558.         EntrySelectTo(entryPtr, index);
  559.     } else {
  560.         Tcl_AppendResult(interp, "bad select option \"", argv[2],
  561.             "\": must be adjust, clear, from, or to", (char *) NULL);
  562.         goto error;
  563.     }
  564.     } else if ((c == 'v') && (strncmp(argv[1], "view", length) == 0)) {
  565.     int index;
  566.  
  567.     if (argc != 3) {
  568.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  569.             argv[0], " view index\"", (char *) NULL);
  570.         goto error;
  571.     }
  572.     if (GetEntryIndex(interp, entryPtr, argv[2], &index) != TCL_OK) {
  573.         goto error;
  574.     }
  575.     if ((index >= entryPtr->numChars) && (index > 0)) {
  576.         index = entryPtr->numChars-1;
  577.     }
  578.     entryPtr->leftIndex = index;
  579.     EventuallyRedraw(entryPtr);
  580.     EntryUpdateScrollbar(entryPtr);
  581.     } else {
  582.     Tcl_AppendResult(interp, "bad option \"", argv[1],
  583.         "\": must be configure, cursor, delete, get, index, ",
  584.         "insert, scan, select, or view", (char *) NULL);
  585.     goto error;
  586.     }
  587.     done:
  588.     Tk_Release((ClientData) entryPtr);
  589.     return result;
  590.  
  591.     error:
  592.     Tk_Release((ClientData) entryPtr);
  593.     return TCL_ERROR;
  594. }
  595.  
  596. /*
  597.  *----------------------------------------------------------------------
  598.  *
  599.  * DestroyEntry --
  600.  *
  601.  *    This procedure is invoked by Tk_EventuallyFree or Tk_Release
  602.  *    to clean up the internal structure of an entry at a safe time
  603.  *    (when no-one is using it anymore).
  604.  *
  605.  * Results:
  606.  *    None.
  607.  *
  608.  * Side effects:
  609.  *    Everything associated with the entry is freed up.
  610.  *
  611.  *----------------------------------------------------------------------
  612.  */
  613.  
  614. static void
  615. DestroyEntry(clientData)
  616.     ClientData clientData;            /* Info about entry widget. */
  617. {
  618.     register Entry *entryPtr = (Entry *) clientData;
  619.  
  620.     ckfree(entryPtr->string);
  621.     if (entryPtr->normalBorder != NULL) {
  622.     Tk_Free3DBorder(entryPtr->normalBorder);
  623.     }
  624.     if (entryPtr->textVarName != NULL) {
  625.     Tcl_UntraceVar(entryPtr->interp, entryPtr->textVarName,
  626.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  627.         EntryTextVarProc, (ClientData) entryPtr);
  628.     ckfree(entryPtr->textVarName);
  629.     }
  630.     if (entryPtr->fontPtr != NULL) {
  631.     Tk_FreeFontStruct(entryPtr->fontPtr);
  632.     }
  633.     if (entryPtr->fgColorPtr != NULL) {
  634.     Tk_FreeColor(entryPtr->fgColorPtr);
  635.     }
  636.     if (entryPtr->textGC != None) {
  637.     Tk_FreeGC(entryPtr->textGC);
  638.     }
  639.     if (entryPtr->selBorder != NULL) {
  640.     Tk_Free3DBorder(entryPtr->selBorder);
  641.     }
  642.     if (entryPtr->selFgColorPtr != NULL) {
  643.     Tk_FreeColor(entryPtr->selFgColorPtr);
  644.     }
  645.     if (entryPtr->selTextGC != None) {
  646.     Tk_FreeGC(entryPtr->selTextGC);
  647.     }
  648.     if (entryPtr->cursorBorder != NULL) {
  649.     Tk_Free3DBorder(entryPtr->cursorBorder);
  650.     }
  651.     Tk_DeleteTimerHandler(entryPtr->cursorBlinkHandler);
  652.     if (entryPtr->cursor != None) {
  653.     Tk_FreeCursor(entryPtr->cursor);
  654.     }
  655.     if (entryPtr->scrollCmd != NULL) {
  656.         ckfree(entryPtr->scrollCmd);
  657.     }
  658.     ckfree((char *) entryPtr);
  659. }
  660.  
  661. /*
  662.  *----------------------------------------------------------------------
  663.  *
  664.  * ConfigureEntry --
  665.  *
  666.  *    This procedure is called to process an argv/argc list, plus
  667.  *    the Tk option database, in order to configure (or reconfigure)
  668.  *    an entry widget.
  669.  *
  670.  * Results:
  671.  *    The return value is a standard Tcl result.  If TCL_ERROR is
  672.  *    returned, then interp->result contains an error message.
  673.  *
  674.  * Side effects:
  675.  *    Configuration information, such as colors, border width,
  676.  *    etc. get set for entryPtr;  old resources get freed,
  677.  *    if there were any.
  678.  *
  679.  *----------------------------------------------------------------------
  680.  */
  681.  
  682. static int
  683. ConfigureEntry(interp, entryPtr, argc, argv, flags)
  684.     Tcl_Interp *interp;        /* Used for error reporting. */
  685.     register Entry *entryPtr;    /* Information about widget;  may or may
  686.                  * not already have values for some fields. */
  687.     int argc;            /* Number of valid entries in argv. */
  688.     char **argv;        /* Arguments. */
  689.     int flags;            /* Flags to pass to Tk_ConfigureWidget. */
  690. {
  691.     XGCValues gcValues;
  692.     GC new;
  693.     int width, height, fontHeight, oldExport;
  694.  
  695.     /*
  696.      * Eliminate any existing trace on a variable monitored by the entry.
  697.      */
  698.  
  699.     if (entryPtr->textVarName != NULL) {
  700.     Tcl_UntraceVar(interp, entryPtr->textVarName, 
  701.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  702.         EntryTextVarProc, (ClientData) entryPtr);
  703.     }
  704.  
  705.     oldExport = entryPtr->exportSelection;
  706.     if (Tk_ConfigureWidget(interp, entryPtr->tkwin, configSpecs,
  707.         argc, argv, (char *) entryPtr, flags) != TCL_OK) {
  708.     return TCL_ERROR;
  709.     }
  710.  
  711.     /*
  712.      * If the entry is tied to the value of a variable, then set up
  713.      * a trace on the variable's value, create the variable if it doesn't
  714.      * exist, and set the entry's value from the variable's value.
  715.      */
  716.  
  717.     if (entryPtr->textVarName != NULL) {
  718.     char *value;
  719.  
  720.     value = Tcl_GetVar(interp, entryPtr->textVarName, TCL_GLOBAL_ONLY);
  721.     if (value == NULL) {
  722.         Tcl_SetVar(interp, entryPtr->textVarName, entryPtr->string,
  723.             TCL_GLOBAL_ONLY);
  724.     } else {
  725.         EntrySetValue(entryPtr, value);
  726.     }
  727.     Tcl_TraceVar(interp, entryPtr->textVarName,
  728.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  729.         EntryTextVarProc, (ClientData) entryPtr);
  730.     }
  731.  
  732.     /*
  733.      * A few other options also need special processing, such as parsing
  734.      * the geometry and setting the background from a 3-D border.
  735.      */
  736.  
  737.     if ((entryPtr->state != tkNormalUid)
  738.         && (entryPtr->state != tkDisabledUid)) {
  739.     Tcl_AppendResult(interp, "bad state value \"", entryPtr->state,
  740.         "\":  must be normal or disabled", (char *) NULL);
  741.     entryPtr->state = tkNormalUid;
  742.     return TCL_ERROR;
  743.     }
  744.  
  745.     Tk_SetBackgroundFromBorder(entryPtr->tkwin, entryPtr->normalBorder);
  746.  
  747.     gcValues.foreground = entryPtr->fgColorPtr->pixel;
  748.     gcValues.font = entryPtr->fontPtr->fid;
  749.     gcValues.graphics_exposures = False;
  750.     new = Tk_GetGC(entryPtr->tkwin, GCForeground|GCFont|GCGraphicsExposures,
  751.         &gcValues);
  752.     if (entryPtr->textGC != None) {
  753.     Tk_FreeGC(entryPtr->textGC);
  754.     }
  755.     entryPtr->textGC = new;
  756.  
  757.     gcValues.foreground = entryPtr->selFgColorPtr->pixel;
  758.     gcValues.font = entryPtr->fontPtr->fid;
  759.     new = Tk_GetGC(entryPtr->tkwin, GCForeground|GCFont, &gcValues);
  760.     if (entryPtr->selTextGC != None) {
  761.     Tk_FreeGC(entryPtr->selTextGC);
  762.     }
  763.     entryPtr->selTextGC = new;
  764.  
  765.     if (entryPtr->cursorWidth > 2*entryPtr->fontPtr->min_bounds.width) {
  766.     entryPtr->cursorWidth = 2*entryPtr->fontPtr->min_bounds.width;
  767.     if (entryPtr->cursorWidth == 0) {
  768.         entryPtr->cursorWidth = 2;
  769.     }
  770.     }
  771.     if (entryPtr->cursorBorderWidth > entryPtr->cursorWidth/2) {
  772.     entryPtr->cursorBorderWidth = entryPtr->cursorWidth/2;
  773.     }
  774.  
  775.     /*
  776.      * Restart the cursor timing sequence in case the on-time or off-time
  777.      * just changed.
  778.      */
  779.  
  780.     if (entryPtr->flags & GOT_FOCUS) {
  781.     EntryFocusProc((ClientData) entryPtr, 1);
  782.     }
  783.  
  784.     /*
  785.      * Claim the selection if we've suddenly started exporting it.
  786.      */
  787.  
  788.     if (entryPtr->exportSelection && (!oldExport)
  789.         && (entryPtr->selectFirst != -1)) {
  790.     Tk_OwnSelection(entryPtr->tkwin, EntryLostSelection,
  791.         (ClientData) entryPtr);
  792.     }
  793.  
  794.     /*
  795.      * Register the desired geometry for the window, and arrange for
  796.      * the window to be redisplayed.
  797.      */
  798.  
  799.     fontHeight = entryPtr->fontPtr->ascent + entryPtr->fontPtr->descent;
  800.     entryPtr->avgWidth = XTextWidth(entryPtr->fontPtr, "0", 1);
  801.     width = entryPtr->prefWidth*entryPtr->avgWidth + (15*fontHeight)/10;
  802.     height = fontHeight + 2*entryPtr->borderWidth + 2;
  803.     Tk_GeometryRequest(entryPtr->tkwin, width, height);
  804.     Tk_SetInternalBorder(entryPtr->tkwin, entryPtr->borderWidth);
  805.     if (entryPtr->relief != TK_RELIEF_FLAT) {
  806.     entryPtr->offset = entryPtr->borderWidth;
  807.     } else {
  808.     entryPtr->offset = 0;
  809.     }
  810.     EventuallyRedraw(entryPtr);
  811.     EntryUpdateScrollbar(entryPtr);
  812.     return TCL_OK;
  813. }
  814.  
  815. /*
  816.  *--------------------------------------------------------------
  817.  *
  818.  * DisplayEntry --
  819.  *
  820.  *    This procedure redraws the contents of an entry window.
  821.  *
  822.  * Results:
  823.  *    None.
  824.  *
  825.  * Side effects:
  826.  *    Information appears on the screen.
  827.  *
  828.  *--------------------------------------------------------------
  829.  */
  830.  
  831. static void
  832. DisplayEntry(clientData)
  833.     ClientData clientData;    /* Information about window. */
  834. {
  835.     register Entry *entryPtr = (Entry *) clientData;
  836.     register Tk_Window tkwin = entryPtr->tkwin;
  837.     int startX, baseY, selStartX, selEndX, index, cursorX;
  838.     int xBound, count;
  839.     Pixmap pixmap;
  840.  
  841.     entryPtr->flags &= ~REDRAW_PENDING;
  842.     if ((entryPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
  843.     return;
  844.     }
  845.  
  846.     /*
  847.      * In order to avoid screen flashes, this procedure redraws the
  848.      * textual area of the entry into off-screen memory, then copies
  849.      * it back on-screen in a single operation.  This means there's
  850.      * no point in time where the on-screen image has been cleared.
  851.      */
  852.  
  853.     pixmap = XCreatePixmap(Tk_Display(tkwin), Tk_WindowId(tkwin),
  854.         Tk_Width(tkwin), Tk_Height(tkwin),
  855.         DefaultDepthOfScreen(Tk_Screen(tkwin)));
  856.  
  857.     /*
  858.      * Compute x-coordinate of the "leftIndex" character, plus limit
  859.      * of visible x-coordinates (actually, pixel just after last visible
  860.      * one), plus vertical position of baseline of text.
  861.      */
  862.  
  863.     startX = entryPtr->offset;
  864.     xBound = Tk_Width(tkwin) - entryPtr->offset;
  865.     baseY = (Tk_Height(tkwin) + entryPtr->fontPtr->ascent
  866.         - entryPtr->fontPtr->descent)/2;
  867.  
  868.     /*
  869.      * Draw the background in three layers.  From bottom to top the
  870.      * layers are:  normal background, selection background, and
  871.      * insertion cursor background.
  872.      */
  873.  
  874.     Tk_Fill3DRectangle(Tk_Display(tkwin), pixmap, entryPtr->normalBorder,
  875.         0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, TK_RELIEF_FLAT);
  876.  
  877.     if (entryPtr->selectLast >= entryPtr->leftIndex) {
  878.     if (entryPtr->selectFirst <= entryPtr->leftIndex) {
  879.         selStartX = startX;
  880.         index = entryPtr->leftIndex;
  881.     } else {
  882.         (void) TkMeasureChars(entryPtr->fontPtr,
  883.             entryPtr->string+entryPtr->leftIndex,
  884.             entryPtr->selectFirst - entryPtr->leftIndex, startX,
  885.             xBound, TK_PARTIAL_OK|TK_NEWLINES_NOT_SPECIAL, &selStartX);
  886.         index = entryPtr->selectFirst;
  887.     }
  888.     if (selStartX < xBound) {
  889.         (void) TkMeasureChars(entryPtr->fontPtr,
  890.             entryPtr->string + index, entryPtr->selectLast +1 - index,
  891.             selStartX, xBound, TK_PARTIAL_OK|TK_NEWLINES_NOT_SPECIAL,
  892.             &selEndX);
  893.         Tk_Fill3DRectangle(Tk_Display(tkwin), pixmap, entryPtr->selBorder,
  894.             selStartX - entryPtr->selBorderWidth,
  895.             baseY - entryPtr->fontPtr->ascent
  896.                 - entryPtr->selBorderWidth,
  897.             (selEndX - selStartX) + 2*entryPtr->selBorderWidth,
  898.             entryPtr->fontPtr->ascent + entryPtr->fontPtr->descent
  899.                 + 2*entryPtr->selBorderWidth,
  900.             entryPtr->selBorderWidth, TK_RELIEF_RAISED);
  901.     } else {
  902.         selEndX = xBound;
  903.     }
  904.     }
  905.  
  906.     /*
  907.      * Draw a special background for the insertion cursor, overriding
  908.      * even the selection background.  As a special hack to keep the
  909.      * cursor visible on mono displays, write background in the cursor
  910.      * area (instead of nothing) when the cursor isn't on.  Otherwise
  911.      * the selection would hide the cursor.
  912.      */
  913.  
  914.     if ((entryPtr->cursorPos >= entryPtr->leftIndex)
  915.         && (entryPtr->state == tkNormalUid)
  916.         && (entryPtr->flags & GOT_FOCUS)) {
  917.     (void) TkMeasureChars(entryPtr->fontPtr,
  918.         entryPtr->string + entryPtr->leftIndex,
  919.         entryPtr->cursorPos - entryPtr->leftIndex, startX,
  920.         xBound, TK_PARTIAL_OK|TK_NEWLINES_NOT_SPECIAL, &cursorX);
  921.     if (cursorX < xBound) {
  922.         if (entryPtr->flags & CURSOR_ON) {
  923.         Tk_Fill3DRectangle(Tk_Display(tkwin), pixmap,
  924.             entryPtr->cursorBorder,
  925.             cursorX - (entryPtr->cursorWidth)/2,
  926.             baseY - entryPtr->fontPtr->ascent,
  927.             entryPtr->cursorWidth,
  928.             entryPtr->fontPtr->ascent + entryPtr->fontPtr->descent,
  929.             entryPtr->cursorBorderWidth, TK_RELIEF_RAISED);
  930.         } else if (DefaultDepthOfScreen(Tk_Screen(tkwin)) == 1) {
  931.         Tk_Fill3DRectangle(Tk_Display(tkwin), pixmap,
  932.             entryPtr->normalBorder,
  933.             cursorX - (entryPtr->cursorWidth)/2,
  934.             baseY - entryPtr->fontPtr->ascent,
  935.             entryPtr->cursorWidth,
  936.             entryPtr->fontPtr->ascent + entryPtr->fontPtr->descent,
  937.             0, TK_RELIEF_FLAT);
  938.         }
  939.     }
  940.     }
  941.  
  942.     /*
  943.      * Draw the text in three pieces:  first the piece to the left of
  944.      * the selection, then the selection, then the piece to the right
  945.      * of the selection.
  946.      */
  947.  
  948.     if (entryPtr->selectLast < entryPtr->leftIndex) {
  949.     TkDisplayChars(Tk_Display(tkwin), pixmap, entryPtr->textGC,
  950.         entryPtr->fontPtr, entryPtr->string + entryPtr->leftIndex,
  951.         entryPtr->numChars - entryPtr->leftIndex, startX, baseY,
  952.         TK_NEWLINES_NOT_SPECIAL);
  953.     } else {
  954.     count = entryPtr->selectFirst - entryPtr->leftIndex;
  955.     if (count > 0) {
  956.         TkDisplayChars(Tk_Display(tkwin), pixmap, entryPtr->textGC,
  957.             entryPtr->fontPtr, entryPtr->string + entryPtr->leftIndex,
  958.             count, startX, baseY, TK_NEWLINES_NOT_SPECIAL);
  959.         index = entryPtr->selectFirst;
  960.     } else {
  961.         index = entryPtr->leftIndex;
  962.     }
  963.     count = entryPtr->selectLast + 1 - index;
  964.     if ((selStartX < xBound) && (count > 0)) {
  965.         TkDisplayChars(Tk_Display(tkwin), pixmap, entryPtr->selTextGC,
  966.             entryPtr->fontPtr, entryPtr->string + index, count,
  967.             selStartX, baseY, TK_NEWLINES_NOT_SPECIAL);
  968.     }
  969.     count = entryPtr->numChars - entryPtr->selectLast - 1;
  970.     if ((selEndX < xBound) && (count > 0)) {
  971.         TkDisplayChars(Tk_Display(tkwin), pixmap, entryPtr->textGC,
  972.             entryPtr->fontPtr,
  973.             entryPtr->string + entryPtr->selectLast + 1,
  974.             count, selEndX, baseY, TK_NEWLINES_NOT_SPECIAL);
  975.     }
  976.     }
  977.  
  978.     /*
  979.      * Draw the border last, so it will overwrite any text that extends
  980.      * past the viewable part of the window.
  981.      */
  982.  
  983.     if (entryPtr->relief != TK_RELIEF_FLAT) {
  984.     Tk_Draw3DRectangle(Tk_Display(tkwin), pixmap,
  985.         entryPtr->normalBorder, 0, 0, Tk_Width(tkwin),
  986.         Tk_Height(tkwin), entryPtr->borderWidth,
  987.         entryPtr->relief);
  988.     }
  989.  
  990.     /*
  991.      * Everything's been redisplayed;  now copy the pixmap onto the screen
  992.      * and free up the pixmap.
  993.      */
  994.  
  995.     XCopyArea(Tk_Display(tkwin), pixmap, Tk_WindowId(tkwin), entryPtr->textGC,
  996.         0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, 0);
  997.     XFreePixmap(Tk_Display(tkwin), pixmap);
  998.     entryPtr->flags &= ~BORDER_NEEDED;
  999. }
  1000.  
  1001. /*
  1002.  *----------------------------------------------------------------------
  1003.  *
  1004.  * InsertChars --
  1005.  *
  1006.  *    Add new characters to an entry widget.
  1007.  *
  1008.  * Results:
  1009.  *    None.
  1010.  *
  1011.  * Side effects:
  1012.  *    New information gets added to entryPtr;  it will be redisplayed
  1013.  *    soon, but not necessarily immediately.
  1014.  *
  1015.  *----------------------------------------------------------------------
  1016.  */
  1017.  
  1018. static void
  1019. InsertChars(entryPtr, index, string)
  1020.     register Entry *entryPtr;    /* Entry that is to get the new
  1021.                  * elements. */
  1022.     int index;            /* Add the new elements before this
  1023.                  * element. */
  1024.     char *string;        /* New characters to add (NULL-terminated
  1025.                  * string). */
  1026. {
  1027.     int length;
  1028.     char *new;
  1029.  
  1030.     length = strlen(string);
  1031.     if (length == 0) {
  1032.     return;
  1033.     }
  1034.     new = (char *) ckalloc((unsigned) (entryPtr->numChars + length + 1));
  1035.     strncpy(new, entryPtr->string, index);
  1036.     strcpy(new+index, string);
  1037.     strcpy(new+index+length, entryPtr->string+index);
  1038.     ckfree(entryPtr->string);
  1039.     entryPtr->string = new;
  1040.     entryPtr->numChars += length;
  1041.  
  1042.     /*
  1043.      * Inserting characters invalidates all indexes into the string.
  1044.      * Touch up the indexes so that they still refer to the same
  1045.      * characters (at new positions).
  1046.      */
  1047.  
  1048.     if (entryPtr->selectFirst >= index) {
  1049.     entryPtr->selectFirst += length;
  1050.     }
  1051.     if (entryPtr->selectLast >= index) {
  1052.     entryPtr->selectLast += length;
  1053.     }
  1054.     if (entryPtr->selectAnchor >= index) {
  1055.     entryPtr->selectAnchor += length;
  1056.     }
  1057.     if (entryPtr->leftIndex > index) {
  1058.     entryPtr->leftIndex += length;
  1059.     }
  1060.     if (entryPtr->cursorPos >= index) {
  1061.     entryPtr->cursorPos += length;
  1062.     }
  1063.  
  1064.     if (entryPtr->textVarName != NULL) {
  1065.     Tcl_SetVar(entryPtr->interp, entryPtr->textVarName, entryPtr->string,
  1066.         TCL_GLOBAL_ONLY);
  1067.     }
  1068.     EventuallyRedraw(entryPtr);
  1069.     EntryUpdateScrollbar(entryPtr);
  1070. }
  1071.  
  1072. /*
  1073.  *----------------------------------------------------------------------
  1074.  *
  1075.  * DeleteChars --
  1076.  *
  1077.  *    Remove one or more characters from an entry widget.
  1078.  *
  1079.  * Results:
  1080.  *    None.
  1081.  *
  1082.  * Side effects:
  1083.  *    Memory gets freed, the entry gets modified and (eventually)
  1084.  *    redisplayed.
  1085.  *
  1086.  *----------------------------------------------------------------------
  1087.  */
  1088.  
  1089. static void
  1090. DeleteChars(entryPtr, index, count)
  1091.     register Entry *entryPtr;    /* Entry widget to modify. */
  1092.     int index;            /* Index of first character to delete. */
  1093.     int count;            /* How many characters to delete. */
  1094. {
  1095.     char *new;
  1096.  
  1097.     if ((index + count) > entryPtr->numChars) {
  1098.     count = entryPtr->numChars - index;
  1099.     }
  1100.     if (count <= 0) {
  1101.     return;
  1102.     }
  1103.  
  1104.     new = (char *) ckalloc((unsigned) (entryPtr->numChars + 1 - count));
  1105.     strncpy(new, entryPtr->string, index);
  1106.     strcpy(new+index, entryPtr->string+index+count);
  1107.     ckfree(entryPtr->string);
  1108.     entryPtr->string = new;
  1109.     entryPtr->numChars -= count;
  1110.  
  1111.     /*
  1112.      * Deleting characters results in the remaining characters being
  1113.      * renumbered.  Update the various indexes into the string to reflect
  1114.      * this change.
  1115.      */
  1116.     if (entryPtr->selectFirst >= index) {
  1117.     if (entryPtr->selectFirst >= (index+count)) {
  1118.         entryPtr->selectFirst -= count;
  1119.     } else {
  1120.         entryPtr->selectFirst = index;
  1121.     }
  1122.     }
  1123.     if (entryPtr->selectLast >= index) {
  1124.     if (entryPtr->selectLast >= (index+count)) {
  1125.         entryPtr->selectLast -= count;
  1126.     } else {
  1127.         entryPtr->selectLast = index-1;
  1128.     }
  1129.     }
  1130.     if (entryPtr->selectLast < entryPtr->selectFirst) {
  1131.     entryPtr->selectFirst = entryPtr->selectLast = -1;
  1132.     }
  1133.     if (entryPtr->selectAnchor >= index) {
  1134.     if (entryPtr->selectAnchor >= (index+count)) {
  1135.         entryPtr->selectAnchor -= count;
  1136.     } else {
  1137.         entryPtr->selectAnchor = index;
  1138.     }
  1139.     }
  1140.     if (entryPtr->leftIndex > index) {
  1141.     if (entryPtr->leftIndex >= (index+count)) {
  1142.         entryPtr->leftIndex -= count;
  1143.     } else {
  1144.         entryPtr->leftIndex = index;
  1145.     }
  1146.     }
  1147.     if (entryPtr->cursorPos >= index) {
  1148.     if (entryPtr->cursorPos >= (index+count)) {
  1149.         entryPtr->cursorPos -= count;
  1150.     } else {
  1151.         entryPtr->cursorPos = index;
  1152.     }
  1153.     }
  1154.  
  1155.     if (entryPtr->textVarName != NULL) {
  1156.     Tcl_SetVar(entryPtr->interp, entryPtr->textVarName, entryPtr->string,
  1157.         TCL_GLOBAL_ONLY);
  1158.     }
  1159.     EventuallyRedraw(entryPtr);
  1160.     EntryUpdateScrollbar(entryPtr);
  1161. }
  1162.  
  1163. /*
  1164.  *----------------------------------------------------------------------
  1165.  *
  1166.  * EntrySetValue --
  1167.  *
  1168.  *    Replace the contents of a text entry with a given value.  This
  1169.  *    procedure is invoked when updating the entry from the entry's
  1170.  *    associated variable.
  1171.  *
  1172.  * Results:
  1173.  *    None.
  1174.  *
  1175.  * Side effects:
  1176.  *    The string displayed in the entry will change.  Any selection
  1177.  *    in the entry is lost and the insertion point gets set to the
  1178.  *    end of the entry.  Note: this procedure does *not* update the
  1179.  *    entry's associated variable, since that could result in an
  1180.  *    infinite loop.
  1181.  *
  1182.  *----------------------------------------------------------------------
  1183.  */
  1184.  
  1185. static void
  1186. EntrySetValue(entryPtr, value)
  1187.     register Entry *entryPtr;        /* Entry whose value is to be
  1188.                      * changed. */
  1189.     char *value;            /* New text to display in entry. */
  1190. {
  1191.     ckfree(entryPtr->string);
  1192.     entryPtr->numChars = strlen(value);
  1193.     entryPtr->string = (char *) ckalloc((unsigned) (entryPtr->numChars + 1));
  1194.     strcpy(entryPtr->string, value);
  1195.     entryPtr->selectFirst = -1;
  1196.     entryPtr->leftIndex = 0;
  1197.     entryPtr->cursorPos = entryPtr->numChars;
  1198.  
  1199.     EventuallyRedraw(entryPtr);
  1200.     EntryUpdateScrollbar(entryPtr);
  1201. }
  1202.  
  1203. /*
  1204.  *--------------------------------------------------------------
  1205.  *
  1206.  * EntryEventProc --
  1207.  *
  1208.  *    This procedure is invoked by the Tk dispatcher for various
  1209.  *    events on entryes.
  1210.  *
  1211.  * Results:
  1212.  *    None.
  1213.  *
  1214.  * Side effects:
  1215.  *    When the window gets deleted, internal structures get
  1216.  *    cleaned up.  When it gets exposed, it is redisplayed.
  1217.  *
  1218.  *--------------------------------------------------------------
  1219.  */
  1220.  
  1221. static void
  1222. EntryEventProc(clientData, eventPtr)
  1223.     ClientData clientData;    /* Information about window. */
  1224.     XEvent *eventPtr;        /* Information about event. */
  1225. {
  1226.     Entry *entryPtr = (Entry *) clientData;
  1227.     if (eventPtr->type == Expose) {
  1228.     EventuallyRedraw(entryPtr);
  1229.     entryPtr->flags |= BORDER_NEEDED;
  1230.     } else if (eventPtr->type == DestroyNotify) {
  1231.     Tcl_DeleteCommand(entryPtr->interp, Tk_PathName(entryPtr->tkwin));
  1232.     entryPtr->tkwin = NULL;
  1233.     if (entryPtr->flags & REDRAW_PENDING) {
  1234.         Tk_CancelIdleCall(DisplayEntry, (ClientData) entryPtr);
  1235.     }
  1236.     Tk_EventuallyFree((ClientData) entryPtr, DestroyEntry);
  1237.     } else if (eventPtr->type == ConfigureNotify) {
  1238.     Tk_Preserve((ClientData) entryPtr);
  1239.     EventuallyRedraw(entryPtr);
  1240.     EntryUpdateScrollbar(entryPtr);
  1241.     Tk_Release((ClientData) entryPtr);
  1242.     }
  1243. }
  1244.  
  1245. /*
  1246.  *--------------------------------------------------------------
  1247.  *
  1248.  * GetEntryIndex --
  1249.  *
  1250.  *    Parse an index into an entry and return either its value
  1251.  *    or an error.
  1252.  *
  1253.  * Results:
  1254.  *    A standard Tcl result.  If all went well, then *indexPtr is
  1255.  *    filled in with the index (into entryPtr) corresponding to
  1256.  *    string.  The index value is guaranteed to lie between 0 and
  1257.  *    the number of characters in the string, inclusive.  If an
  1258.  *    error occurs then an error message is left in interp->result.
  1259.  *
  1260.  * Side effects:
  1261.  *    None.
  1262.  *
  1263.  *--------------------------------------------------------------
  1264.  */
  1265.  
  1266. static int
  1267. GetEntryIndex(interp, entryPtr, string, indexPtr)
  1268.     Tcl_Interp *interp;        /* For error messages. */
  1269.     Entry *entryPtr;        /* Entry for which the index is being
  1270.                  * specified. */
  1271.     char *string;        /* Specifies character in entryPtr. */
  1272.     int *indexPtr;        /* Where to store converted index. */
  1273. {
  1274.     int length;
  1275.  
  1276.     length = strlen(string);
  1277.  
  1278.     if (string[0] == 'e') {
  1279.     if (strncmp(string, "end", length) == 0) {
  1280.         *indexPtr = entryPtr->numChars;
  1281.     } else {
  1282.         badIndex:
  1283.  
  1284.         /*
  1285.          * Some of the paths here leave messages in interp->result,
  1286.          * so we have to clear it out before storing our own message.
  1287.          */
  1288.  
  1289.         Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);
  1290.         Tcl_AppendResult(interp, "bad entry index \"", string,
  1291.             "\"", (char *) NULL);
  1292.         return TCL_ERROR;
  1293.     }
  1294.     } else if (string[0] == 'c') {
  1295.     if (strncmp(string, "cursor", length) == 0) {
  1296.         *indexPtr = entryPtr->cursorPos;
  1297.     } else {
  1298.         goto badIndex;
  1299.     }
  1300.     } else if (string[0] == 's') {
  1301.     if (entryPtr->selectFirst == -1) {
  1302.         interp->result = "selection isn't in entry";
  1303.         return TCL_ERROR;
  1304.     }
  1305.     if (length < 5) {
  1306.         goto badIndex;
  1307.     }
  1308.     if (strncmp(string, "sel.first", length) == 0) {
  1309.         *indexPtr = entryPtr->selectFirst;
  1310.     } else if (strncmp(string, "sel.last", length) == 0) {
  1311.         *indexPtr = entryPtr->selectLast;
  1312.     } else {
  1313.         goto badIndex;
  1314.     }
  1315.     } else if (string[0] == '@') {
  1316.     int x, dummy;
  1317.  
  1318.     if (Tcl_GetInt(interp, string+1, &x) != TCL_OK) {
  1319.         goto badIndex;
  1320.     }
  1321.     if (entryPtr->numChars == 0) {
  1322.         *indexPtr = 0;
  1323.     } else {
  1324.         *indexPtr = entryPtr->leftIndex + TkMeasureChars(entryPtr->fontPtr,
  1325.             entryPtr->string + entryPtr->leftIndex,
  1326.             entryPtr->numChars - entryPtr->leftIndex,
  1327.             entryPtr->offset, x, TK_NEWLINES_NOT_SPECIAL, &dummy);
  1328.     }
  1329.     } else {
  1330.     if (Tcl_GetInt(interp, string, indexPtr) != TCL_OK) {
  1331.         goto badIndex;
  1332.     }
  1333.     if (*indexPtr < 0){
  1334.         *indexPtr = 0;
  1335.     } else if (*indexPtr > entryPtr->numChars) {
  1336.         *indexPtr = entryPtr->numChars;
  1337.     }
  1338.     }
  1339.     return TCL_OK;
  1340. }
  1341.  
  1342. /*
  1343.  *----------------------------------------------------------------------
  1344.  *
  1345.  * EntryScanTo --
  1346.  *
  1347.  *    Given a y-coordinate (presumably of the curent mouse location)
  1348.  *    drag the view in the window to implement the scan operation.
  1349.  *
  1350.  * Results:
  1351.  *    None.
  1352.  *
  1353.  * Side effects:
  1354.  *    The view in the window may change.
  1355.  *
  1356.  *----------------------------------------------------------------------
  1357.  */
  1358.  
  1359. static void
  1360. EntryScanTo(entryPtr, x)
  1361.     register Entry *entryPtr;        /* Information about widget. */
  1362.     int x;                /* X-coordinate to use for scan
  1363.                      * operation. */
  1364. {
  1365.     int newLeftIndex;
  1366.  
  1367.     /*
  1368.      * Compute new leftIndex for entry by amplifying the difference
  1369.      * between the current position and the place where the scan
  1370.      * started (the "mark" position).  If we run off the left or right
  1371.      * side of the entry, then reset the mark point so that the current
  1372.      * position continues to correspond to the edge of the window.
  1373.      * This means that the picture will start dragging as soon as the
  1374.      * mouse reverses direction (without this reset, might have to slide
  1375.      * mouse a long ways back before the picture starts moving again).
  1376.      */
  1377.  
  1378.     newLeftIndex = entryPtr->scanMarkIndex
  1379.         - (10*(x - entryPtr->scanMarkX))/entryPtr->avgWidth;
  1380.     if (newLeftIndex >= entryPtr->numChars) {
  1381.     newLeftIndex = entryPtr->scanMarkIndex = entryPtr->numChars-1;
  1382.     entryPtr->scanMarkX = x;
  1383.     }
  1384.     if (newLeftIndex < 0) {
  1385.     newLeftIndex = entryPtr->scanMarkIndex = 0;
  1386.     entryPtr->scanMarkX = x;
  1387.     } 
  1388.     if (newLeftIndex != entryPtr->leftIndex) {
  1389.     entryPtr->leftIndex = newLeftIndex;
  1390.     EventuallyRedraw(entryPtr);
  1391.     EntryUpdateScrollbar(entryPtr);
  1392.     }
  1393. }
  1394.  
  1395. /*
  1396.  *----------------------------------------------------------------------
  1397.  *
  1398.  * EntrySelectTo --
  1399.  *
  1400.  *    Modify the selection by moving its un-anchored end.  This could
  1401.  *    make the selection either larger or smaller.
  1402.  *
  1403.  * Results:
  1404.  *    None.
  1405.  *
  1406.  * Side effects:
  1407.  *    The selection changes.
  1408.  *
  1409.  *----------------------------------------------------------------------
  1410.  */
  1411.  
  1412. static void
  1413. EntrySelectTo(entryPtr, index)
  1414.     register Entry *entryPtr;        /* Information about widget. */
  1415.     int index;                /* Index of element that is to
  1416.                      * become the "other" end of the
  1417.                      * selection. */
  1418. {
  1419.     int newFirst, newLast;
  1420.  
  1421.     /*
  1422.      * Grab the selection if we don't own it already.
  1423.      */
  1424.  
  1425.     if ((entryPtr->selectFirst == -1) && (entryPtr->exportSelection)) {
  1426.     Tk_OwnSelection(entryPtr->tkwin, EntryLostSelection,
  1427.         (ClientData) entryPtr);
  1428.     }
  1429.  
  1430.     if (index < 0) {
  1431.     index = 0;
  1432.     }
  1433.     if (index >= entryPtr->numChars) {
  1434.     index = entryPtr->numChars-1;
  1435.     }
  1436.     if (entryPtr->selectAnchor > entryPtr->numChars) {
  1437.     entryPtr->selectAnchor = entryPtr->numChars;
  1438.     }
  1439.     if (entryPtr->selectAnchor <= index) {
  1440.     newFirst = entryPtr->selectAnchor;
  1441.     newLast = index;
  1442.     } else {
  1443.     newFirst = index;
  1444.     newLast = entryPtr->selectAnchor - 1;
  1445.     if (newLast < 0) {
  1446.         newFirst = newLast = -1;
  1447.     }
  1448.     }
  1449.     if ((entryPtr->selectFirst == newFirst)
  1450.         && (entryPtr->selectLast == newLast)) {
  1451.     return;
  1452.     }
  1453.     entryPtr->selectFirst = newFirst;
  1454.     entryPtr->selectLast = newLast;
  1455.     EventuallyRedraw(entryPtr);
  1456. }
  1457.  
  1458. /*
  1459.  *----------------------------------------------------------------------
  1460.  *
  1461.  * EntryFetchSelection --
  1462.  *
  1463.  *    This procedure is called back by Tk when the selection is
  1464.  *    requested by someone.  It returns part or all of the selection
  1465.  *    in a buffer provided by the caller.
  1466.  *
  1467.  * Results:
  1468.  *    The return value is the number of non-NULL bytes stored
  1469.  *    at buffer.  Buffer is filled (or partially filled) with a
  1470.  *    NULL-terminated string containing part or all of the selection,
  1471.  *    as given by offset and maxBytes.
  1472.  *
  1473.  * Side effects:
  1474.  *    None.
  1475.  *
  1476.  *----------------------------------------------------------------------
  1477.  */
  1478.  
  1479. static int
  1480. EntryFetchSelection(clientData, offset, buffer, maxBytes)
  1481.     ClientData clientData;        /* Information about entry widget. */
  1482.     int offset;                /* Offset within selection of first
  1483.                      * character to be returned. */
  1484.     char *buffer;            /* Location in which to place
  1485.                      * selection. */
  1486.     int maxBytes;            /* Maximum number of bytes to place
  1487.                      * at buffer, not including terminating
  1488.                      * NULL character. */
  1489. {
  1490.     Entry *entryPtr = (Entry *) clientData;
  1491.     int count;
  1492.  
  1493.     if ((entryPtr->selectFirst < 0) || !(entryPtr->exportSelection)) {
  1494.     return -1;
  1495.     }
  1496.     count = entryPtr->selectLast + 1 - entryPtr->selectFirst - offset;
  1497.     if (count > maxBytes) {
  1498.     count = maxBytes;
  1499.     }
  1500.     if (count <= 0) {
  1501.     return 0;
  1502.     }
  1503.     strncpy(buffer, entryPtr->string + entryPtr->selectFirst + offset, count);
  1504.     buffer[count] = '\0';
  1505.     return count;
  1506. }
  1507.  
  1508. /*
  1509.  *----------------------------------------------------------------------
  1510.  *
  1511.  * EntryLostSelection --
  1512.  *
  1513.  *    This procedure is called back by Tk when the selection is
  1514.  *    grabbed away from an entry widget.
  1515.  *
  1516.  * Results:
  1517.  *    None.
  1518.  *
  1519.  * Side effects:
  1520.  *    The existing selection is unhighlighted, and the window is
  1521.  *    marked as not containing a selection.
  1522.  *
  1523.  *----------------------------------------------------------------------
  1524.  */
  1525.  
  1526. static void
  1527. EntryLostSelection(clientData)
  1528.     ClientData clientData;        /* Information about entry widget. */
  1529. {
  1530.     Entry *entryPtr = (Entry *) clientData;
  1531.  
  1532.     if ((entryPtr->selectFirst != -1) && entryPtr->exportSelection) {
  1533.     entryPtr->selectFirst = -1;
  1534.     entryPtr->selectLast = -1;
  1535.     EventuallyRedraw(entryPtr);
  1536.     }
  1537. }
  1538.  
  1539. /*
  1540.  *----------------------------------------------------------------------
  1541.  *
  1542.  * EventuallyRedraw --
  1543.  *
  1544.  *    Ensure that an entry is eventually redrawn on the display.
  1545.  *
  1546.  * Results:
  1547.  *    None.
  1548.  *
  1549.  * Side effects:
  1550.  *    Information gets redisplayed.  Right now we don't do selective
  1551.  *    redisplays:  the whole window will be redrawn.  This doesn't
  1552.  *    seem to hurt performance noticeably, but if it does then this
  1553.  *    could be changed.
  1554.  *
  1555.  *----------------------------------------------------------------------
  1556.  */
  1557.  
  1558. static void
  1559. EventuallyRedraw(entryPtr)
  1560.     register Entry *entryPtr;        /* Information about widget. */
  1561. {
  1562.     if ((entryPtr->tkwin == NULL) || !Tk_IsMapped(entryPtr->tkwin)) {
  1563.     return;
  1564.     }
  1565.  
  1566.     /*
  1567.      * Right now we don't do selective redisplays:  the whole window
  1568.      * will be redrawn.  This doesn't seem to hurt performance noticeably,
  1569.      * but if it does then this could be changed.
  1570.      */
  1571.  
  1572.     if (!(entryPtr->flags & REDRAW_PENDING)) {
  1573.     entryPtr->flags |= REDRAW_PENDING;
  1574.     Tk_DoWhenIdle(DisplayEntry, (ClientData) entryPtr);
  1575.     }
  1576. }
  1577.  
  1578. /*
  1579.  *----------------------------------------------------------------------
  1580.  *
  1581.  * EntryUpdateScrollbar --
  1582.  *
  1583.  *    This procedure is invoked whenever information has changed in
  1584.  *    an entry in a way that would invalidate a scrollbar display.
  1585.  *    If there is an associated scrollbar, then this command updates
  1586.  *    it by invoking a Tcl command.
  1587.  *
  1588.  * Results:
  1589.  *    None.
  1590.  *
  1591.  * Side effects:
  1592.  *    A Tcl command is invoked, and an additional command may be
  1593.  *    invoked to process errors in the command.
  1594.  *
  1595.  *----------------------------------------------------------------------
  1596.  */
  1597.  
  1598. static void
  1599. EntryUpdateScrollbar(entryPtr)
  1600.     register Entry *entryPtr;        /* Information about widget. */
  1601. {
  1602.     char args[100];
  1603.     int result, last, charsInWindow, endX;
  1604.  
  1605.     if (entryPtr->scrollCmd == NULL) {
  1606.     return;
  1607.     }
  1608.  
  1609.     /*
  1610.      * The most painful part here is guessing how many characters
  1611.      * actually fit in the window.  This is only an estimate in the
  1612.      * case where the window isn't completely filled with characters.
  1613.      */
  1614.  
  1615.     charsInWindow = TkMeasureChars(entryPtr->fontPtr,
  1616.         entryPtr->string + entryPtr->leftIndex,
  1617.         entryPtr->numChars - entryPtr->leftIndex, entryPtr->offset,
  1618.         Tk_Width(entryPtr->tkwin),
  1619.         TK_AT_LEAST_ONE|TK_NEWLINES_NOT_SPECIAL, &endX);
  1620.     if (charsInWindow == 0) {
  1621.     last = entryPtr->leftIndex;
  1622.     } else {
  1623.     last = entryPtr->leftIndex + charsInWindow - 1;
  1624.     }
  1625.     if (endX < Tk_Width(entryPtr->tkwin)) {
  1626.     charsInWindow += (Tk_Width(entryPtr->tkwin) - endX)/entryPtr->avgWidth;
  1627.     }
  1628.     sprintf(args, " %d %d %d %d", entryPtr->numChars, charsInWindow,
  1629.         entryPtr->leftIndex, last);
  1630.     result = Tcl_VarEval(entryPtr->interp, entryPtr->scrollCmd, args,
  1631.         (char *) NULL);
  1632.     if (result != TCL_OK) {
  1633.     TkBindError(entryPtr->interp);
  1634.     }
  1635.     Tcl_SetResult(entryPtr->interp, (char *) NULL, TCL_STATIC);
  1636. }
  1637.  
  1638. /*
  1639.  *----------------------------------------------------------------------
  1640.  *
  1641.  * EntryBlinkProc --
  1642.  *
  1643.  *    This procedure is called as a timer handler to blink the
  1644.  *    insertion cursor off and on.
  1645.  *
  1646.  * Results:
  1647.  *    None.
  1648.  *
  1649.  * Side effects:
  1650.  *    The cursor gets turned on or off, redisplay gets invoked,
  1651.  *    and this procedure reschedules itself.
  1652.  *
  1653.  *----------------------------------------------------------------------
  1654.  */
  1655.  
  1656. static void
  1657. EntryBlinkProc(clientData)
  1658.     ClientData clientData;    /* Pointer to record describing entry. */
  1659. {
  1660.     register Entry *entryPtr = (Entry *) clientData;
  1661.  
  1662.     if (!(entryPtr->flags & GOT_FOCUS) || (entryPtr->cursorOffTime == 0)) {
  1663.     return;
  1664.     }
  1665.     if (entryPtr->flags & CURSOR_ON) {
  1666.     entryPtr->flags &= ~CURSOR_ON;
  1667.     entryPtr->cursorBlinkHandler = Tk_CreateTimerHandler(
  1668.         entryPtr->cursorOffTime, EntryBlinkProc, (ClientData) entryPtr);
  1669.     } else {
  1670.     entryPtr->flags |= CURSOR_ON;
  1671.     entryPtr->cursorBlinkHandler = Tk_CreateTimerHandler(
  1672.         entryPtr->cursorOnTime, EntryBlinkProc, (ClientData) entryPtr);
  1673.     }
  1674.     EventuallyRedraw(entryPtr);
  1675. }
  1676.  
  1677. /*
  1678.  *----------------------------------------------------------------------
  1679.  *
  1680.  * EntryFocusProc --
  1681.  *
  1682.  *    This procedure is called whenever the entry gets or loses the
  1683.  *    input focus.  It's also called whenever the window is reconfigured
  1684.  *    while it has the focus.
  1685.  *
  1686.  * Results:
  1687.  *    None.
  1688.  *
  1689.  * Side effects:
  1690.  *    The cursor gets turned on or off.
  1691.  *
  1692.  *----------------------------------------------------------------------
  1693.  */
  1694.  
  1695. static void
  1696. EntryFocusProc(clientData, gotFocus)
  1697.     ClientData clientData;    /* Pointer to structure describing entry. */
  1698.     int gotFocus;        /* 1 means window is getting focus, 0 means
  1699.                  * it's losing it. */
  1700. {
  1701.     register Entry *entryPtr = (Entry *) clientData;
  1702.  
  1703.     Tk_DeleteTimerHandler(entryPtr->cursorBlinkHandler);
  1704.     if (gotFocus) {
  1705.     entryPtr->flags |= GOT_FOCUS | CURSOR_ON;
  1706.     if (entryPtr->cursorOffTime != 0) {
  1707.         entryPtr->cursorBlinkHandler = Tk_CreateTimerHandler(
  1708.             entryPtr->cursorOnTime, EntryBlinkProc,
  1709.             (ClientData) entryPtr);
  1710.     }
  1711.     } else {
  1712.     entryPtr->flags &= ~(GOT_FOCUS | CURSOR_ON);
  1713.     entryPtr->cursorBlinkHandler = (Tk_TimerToken) NULL;
  1714.     }
  1715.     EventuallyRedraw(entryPtr);
  1716. }
  1717.  
  1718. /*
  1719.  *--------------------------------------------------------------
  1720.  *
  1721.  * EntryTextVarProc --
  1722.  *
  1723.  *    This procedure is invoked when someone changes the variable
  1724.  *    whose contents are to be displayed in an entry.
  1725.  *
  1726.  * Results:
  1727.  *    NULL is always returned.
  1728.  *
  1729.  * Side effects:
  1730.  *    The text displayed in the entry will change to match the
  1731.  *    variable.
  1732.  *
  1733.  *--------------------------------------------------------------
  1734.  */
  1735.  
  1736.     /* ARGSUSED */
  1737. static char *
  1738. EntryTextVarProc(clientData, interp, name1, name2, flags)
  1739.     ClientData clientData;    /* Information about button. */
  1740.     Tcl_Interp *interp;        /* Interpreter containing variable. */
  1741.     char *name1;        /* Name of variable. */
  1742.     char *name2;        /* Second part of variable name. */
  1743.     int flags;            /* Information about what happened. */
  1744. {
  1745.     register Entry *entryPtr = (Entry *) clientData;
  1746.     char *value;
  1747.  
  1748.     /*
  1749.      * If the variable is unset, then immediately recreate it unless
  1750.      * the whole interpreter is going away.
  1751.      */
  1752.  
  1753.     if (flags & TCL_TRACE_UNSETS) {
  1754.     if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
  1755.         Tcl_SetVar2(interp, name1, name2, entryPtr->string,
  1756.             flags & TCL_GLOBAL_ONLY);
  1757.         Tcl_TraceVar2(interp, name1, name2,
  1758.             TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  1759.             EntryTextVarProc, clientData);
  1760.     }
  1761.     return (char *) NULL;
  1762.     }
  1763.  
  1764.     /*
  1765.      * Update the entry's text with the value of the variable, unless
  1766.      * the entry already has that value (this happens when the variable
  1767.      * changes value because we changed it because someone typed in
  1768.      * the entry).
  1769.      */
  1770.  
  1771.     value = Tcl_GetVar2(interp, name1, name2, flags & TCL_GLOBAL_ONLY);
  1772.     if (value == NULL) {
  1773.     value = "";
  1774.     }
  1775.     if (strcmp(value, entryPtr->string) != 0) {
  1776.     EntrySetValue(entryPtr, value);
  1777.     }
  1778.     return (char *) NULL;
  1779. }
  1780.